package com.piece.file.client.s3;

import com.piece.core.framework.exception.ErrorCode;
import com.piece.core.framework.exception.FrameWorkException;
import com.piece.core.framework.util.file.FileUtil;
import com.piece.core.framework.util.math.SequenceUtil;
import com.piece.core.framework.util.net.HttpUtil;
import com.piece.core.framework.util.security.coder.MD5Coder;
import com.piece.core.framework.util.string.StringUtil;
import com.piece.file.client.AbstractFileClient;
import com.piece.file.model.TbSysFile;
import io.minio.*;
import lombok.extern.slf4j.Slf4j;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.List;

import static com.piece.file.client.s3.S3FileClientConfig.ENDPOINT_ALIYUN;

/**
 * 支持S3协议文件客户端
 * 实现 MinIO、阿里云、腾讯云、七牛云、华为云等云服务
 */
@Slf4j
public class S3FileClient extends AbstractFileClient<S3FileClientConfig> {

    private MinioClient client;

    public S3FileClient(String name, S3FileClientConfig config) {
        super(name, config);
    }

    @Override
    protected void doInit() {
        // 补全 domain
        if (StringUtil.isEmpty(config.getDomain())) {
            config.setDomain(buildDomain());
        }
        // 初始化客户端
        client = MinioClient.builder()
                .endpoint(buildEndpointURL())
                .region(buildRegion())
                .credentials(config.getAccessKey(), config.getAccessSecret())
                .build();
    }

    /**
     * 基于bucket + endpoint构建访问的Domain地址
     */
    private String buildDomain() {
        if (HttpUtil.isHttp(config.getEndpoint()) || HttpUtil.isHttps(config.getEndpoint())) {
            return StringUtil.format("{}/{}", config.getEndpoint(), config.getBucket());
        }

        return StringUtil.format("https://{}.{}", config.getBucket(), config.getEndpoint());
    }

    /**
     * 基于endpoint构建调用云服务的URL地址
     */
    private String buildEndpointURL() {
        if (HttpUtil.isHttp(config.getEndpoint()) || HttpUtil.isHttps(config.getEndpoint())) {
            return config.getEndpoint();
        }
        return StringUtil.format("https://{}", config.getEndpoint());
    }

    /**
     * 基于bucket构建region地区
     */
    private String buildRegion() {
        // 阿里云必须有 region，否则会报错
        if (config.getEndpoint().contains(ENDPOINT_ALIYUN)) {
            return StringUtil.subBefore(config.getEndpoint(), '.', false)
                    .replaceAll("-internal", "");
        }
        return null;
    }

    @Override
    public TbSysFile writeToServerStore(byte[] bytes, String path, String name, String md5, boolean stat) throws Exception {
        String serverPath = path + "_" + SequenceUtil.next() + "." + FileUtil.getFileSuffix(name);
        return write(bytes, serverPath, name, md5);
    }

    private TbSysFile write(byte[] bytes, String serverPath, String name, String md5) {
        TbSysFile file = new TbSysFile();
        file.setMd5(md5);
        try {
            List<TbSysFile> existFiles = tbSysFileService.findFileByMd5(md5);
            if (null != existFiles && existFiles.size() > 0) {
                file.setServerPath(existFiles.get(0).getServerPath());
                file.setServerFileName(existFiles.get(0).getServerFileName());
                return file;
            }

            // 执行上传
            client.putObject(PutObjectArgs.builder()
                    .bucket(config.getBucket())
                    .object(serverPath)
                    .stream(new ByteArrayInputStream(bytes), bytes.length, -1)
                    .build());

            file.setServerPath(serverPath);
            file.setServerFileName(name);
            return file;
        } catch (Exception e) {
            log.error("上传S3协议文件异常：{}", e.getMessage());
            throw new FrameWorkException(ErrorCode.UPLOAD);
        }
    }

    @Override
    public InputStream downloadFile(TbSysFile file) throws Exception {
        String serverPath = file.getServerPath();
        try {
            GetObjectResponse response = client.getObject(GetObjectArgs.builder()
                    .bucket(config.getBucket())
                    .object(serverPath)
                    .build());
            return new ByteArrayInputStream(FileUtil.toByte(response));
        } catch (Exception e) {
            log.error("下载S3协议文件异常：{}", e.getMessage());
            throw new FrameWorkException(ErrorCode.DOWNLOAD);
        }
    }

    @Override
    public void updateFile(TbSysFile file) {
        try {
            // 删除原文件
            deleteServerFile(file);

            byte[] bytes = FileUtil.toByte(file.getFileInputStream());
            String md5 = MD5Coder.md5File(bytes);
            // 重新上传文件
            write(bytes, file.getServerPath(), file.getServerFileName(), md5);
        } catch (Exception e) {
            log.error("更新S3协议文件异常：{}", e.getMessage());
            throw new FrameWorkException(new ErrorCode(e.getMessage()));
        }
    }

    @Override
    public void deleteFile(TbSysFile file) throws Exception {
        deleteTbSysFile(file);
        deleteServerFile(file);
    }

    private void deleteServerFile(TbSysFile file) {
        if (checkMd5(file)) {
            return;
        }

        try {
            client.removeObject(RemoveObjectArgs.builder()
                    .bucket(config.getBucket())
                    .object(file.getServerPath())
                    .build());
        } catch (Exception e) {
            log.error("删除S3协议文件异常：{}", e.getMessage());
            throw new FrameWorkException(ErrorCode.DELETE);
        }
    }
}
